Skip to content

feat(web): add sales-demo organizations with admin create and reset - #5459

Merged
iscekic merged 12 commits into
mainfrom
demo-org-e8bd
Aug 25, 2026
Merged

feat(web): add sales-demo organizations with admin create and reset#5459
iscekic merged 12 commits into
mainfrom
demo-org-e8bd

Conversation

@iscekic

@iscekic iscekic commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • An admin can create a demo organization from the admin organizations page. The form accepts one owner email; only @kilocode.ai and @anaconda.com addresses qualify.
  • A new demo organization opens with a $50 balance, one owner plus 25 preset demo members, and a one-year active window.
  • Creating a demo for an owner who already has one keeps the form open and shows a link to the existing demo organization.
  • An admin can reset a demo organization from its detail page, and every day all live demo organizations reset themselves. A reset restores the $50 balance and the owner plus 25 members and clears usage.
  • Demo organizations appear with paying organizations in the admin list, not with trials.
  • The demo organization home omits the seat usage card and the welcome banner.
  • A demo owner or member who signs in lands directly on the demo organization home.

The organization settings schema gains is_sales_demo and sales_demo_last_reset_at, and the organizations table gains a partial unique index UQ_organizations_live_sales_demo_per_owner so an owner can hold one live demo. A new non-self-service credit category sales-demo funds the $50 grant, so it never appears as a user-redeemable promo code. Existing organizations carry neither settings field, so the absent key reads as not-a-demo and no stored row changes.

Files
  • packages/db/src/schema.ts — adds the partial unique index on created_by_kilo_user_id where is_sales_demo is true and deleted_at is null.
  • packages/db/src/schema-types.ts — adds is_sales_demo and sales_demo_last_reset_at to the organization settings schema.
  • apps/web/src/lib/promoCreditCategories.ts — registers the non-self-service sales-demo credit category.

createSalesDemoOrganization inserts the enterprise demo organization, seeds the owner plus 25 stable demo members, and grants a $50 sales-demo credit; it throws ALREADY_OWNS_DEMO when the owner already has a live demo, caught after a pre-check and again after the onConflictDoNothing insert. restoreSalesDemoOrganization clears usage, credit, invitation, and daily-usage rows, zeroes balances, disables auto top-up, re-seeds the owner plus 25 members, and re-grants $50 under a FOR UPDATE row lock; it throws NOT_LIVE_SALES_DEMO for any organization that is not a live demo. Demo settings disable usage limits and trial messaging and store sales_demo_last_reset_at.

Files
  • apps/web/src/lib/organizations/sales-demo.ts — create and restore transactions; clears microdollar_usage, exa_usage_log, compute_usage_charge, credit_transactions, organization_invitations, organization_user_usage, and microdollar_usage_daily; re-seeds and re-grants; defines both sentinels.

Two admin procedures salesDemo.create and salesDemo.reset wrap the lifecycle over tRPC. Create returns BAD_REQUEST for a non-allowlisted email, NOT_FOUND for an unknown owner, and CONFLICT whose message packs the live demo id and name; reset maps NOT_LIVE_SALES_DEMO to NOT_FOUND. The allowlist lives in a module free of server-only imports so the dialog and the router share one rule.

Files
  • apps/web/src/routers/admin/sales-demo-router.tssalesDemo.create and salesDemo.reset mutations and their error mapping.
  • apps/web/src/lib/organizations/sales-demo-email.ts — shared allowlist domain matcher.
  • apps/web/src/routers/admin-router.ts — mounts salesDemo on the admin router.

The cron route GET /api/cron/sales-demo-reset resets every live demo organization and answers {reset, failed}. It requires a Bearer CRON_SECRET, skips an organization whose owner is missing, and reports failures to Sentry without aborting the sweep. Vercel invokes it daily at 0 5 * * *.

Files
  • apps/web/src/app/api/cron/sales-demo-reset/route.ts — cron handler that iterates live demos and returns reset and failed counts.
  • apps/web/vercel.json — adds the /api/cron/sales-demo-reset cron entry.

The admin organization list now treats sales-demo organizations as paying and removes them from trial, and demo organizations bypass any Stripe-status filter. The trial exclusion uses IS DISTINCT FROM 'true' so an absent settings key cannot leak a demo into the trial list; the paying and Stripe-status inclusions match = 'true'.

Files
  • apps/web/src/routers/organizations/organization-admin-router.ts — mode filters and Stripe-status inclusions for sales-demo organizations.

Sign-in landing, the disabled-personal-account landing, the profile organization list, and the client sidebar choose a live sales-demo organization before the oldest. A shared compareOrganizationsForDefault comparator and an isSalesDemo projection drive the ordering, so the server redirect and the sidebar agree.

Files
  • apps/web/src/lib/organizations/sales-demo-sort.ts — shared default-selection comparator; a sales-demo organization always wins.
  • apps/web/src/lib/user/server.ts — demo-first landing and disabled-account routing via the shared comparator.
  • apps/web/src/lib/organizations/organizations.ts — exposes isSalesDemo and orders getProfileOrganizations demo-first.
  • apps/web/src/lib/organizations/organization-types.ts — adds isSalesDemo to UserOrganizationWithSeats.
  • apps/web/src/app/(app)/components/AppSidebar.tsx — sorts the default organization with the shared comparator.

The admin organizations page gains a create-demo dialog, and the admin detail page gains a reset card that renders only for demo organizations. The organizations table takes an actions slot so the page passes the dialog trigger without the table knowing the dialog.

Files
  • apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx — create form, client email validation, and the conflict link to the existing demo.
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminSalesDemoReset.tsx — reset confirm dialog, hidden for non-demos.
  • apps/web/src/app/admin/components/OrganizationsTable.tsx — adds the actions slot.
  • apps/web/src/app/admin/organizations/page.tsx — passes the create-demo button.
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminDashboard.tsx — renders the reset card.

The demo organization home hides the welcome banner and the seat usage card. Seat usage now returns nothing while the organization query loads, surfaces an error card when that query fails, and hides for a suppressed-trial or sales-demo organization before any seat-usage loading or error state.

Files
  • apps/web/src/components/organizations/OrganizationDashboard.tsx — skips the welcome banner for sales-demo organizations.
  • apps/web/src/components/organizations/SeatUsageCard.tsx — hides for suppressed-trial and sales-demo organizations; adds an organization-error card and drops the pre-organization loading card.

Tests: 8 test files changed (6 added, 2 updated).
Generated: 3 files — 0225_rare_captain_britain.sql, 0225_snapshot.json, and an updated _journal.json.


Verification

The web platform ran the six scenarios across five rounds, web-r1 through web-r4-cont.

Case What it proves Platform Result
S1 target sign-in Fake login signs the sales target in and lands on the signed-in profile page. web passed
S2 admin create happy Admin create yields one enterprise org with $50.00 and 26 members and opens its detail. web passed
S3 admin create conflict A second create for an existing owner keeps the dialog open and shows an inline link to the live org. web passed
S4 admin reset baseline Reset keeps the org id and restores $50.00, the owner plus 25 members, and cleared usage. web passed
S5 target landing home Sign-in without a callback lands the owner on the demo org home with no Seat Usage card. web passed
S6 cold-start demo home A cold start of the home never shows a Seat Usage card or an invite banner. web passed

The rounds reproduced no defect on the unfixed build; every case passed on head 902f351.

No recording exists.

Visual Changes

Admin organization list. The list header now holds a Create Demo Organization button in the primary yellow fill. In the picture, the button sits at the top right of the page, above the organization rows.

s2-org-list-header.png

Admin organization detail. The detail now holds a Reset demo organization card in the credit column, and a reset restores the baseline. In the picture, the reset card sits in the right column below Nullify Credits, and the balance reads $50.00.

s4-post-reset.png

Create dialog, conflict state. A second create for an existing owner now shows an inline link to the live demo organization instead of a toast or navigation. In the picture, the dialog stays open, the Email field keeps the address, and the yellow org-name link wraps to two lines below the field.

s3-conflict-dialog.png

Demo organization home. The owner home now shows the balance and the member list with no Seat Usage card. In the picture, the $50.00 balance card and the Members (26) list sit at the top, and no Seat Usage card appears on the page.

s6-coldstart-home.png

Human Steps

No human steps required.

Reviewer Notes

None.

@iscekic iscekic self-assigned this Aug 25, 2026
@kilo-code-bot

kilo-code-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (8 files)
  • apps/web/src/app/api/cron/sales-demo-reset/route-failure.test.ts
  • apps/web/src/app/api/cron/sales-demo-reset/route.test.ts
  • apps/web/src/app/api/cron/sales-demo-reset/route.ts
  • apps/web/src/lib/organizations/sales-demo.ts
  • apps/web/src/routers/admin/sales-demo-router.test.ts
  • packages/db/src/migrations/0224_nice_black_widow.sql
  • packages/db/src/migrations/meta/_journal.json
  • packages/db/src/schema.ts
Previous Review Summaries (4 snapshots, latest commit 60682cb)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 60682cb)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/organizations/sales-demo.ts 213 Restore leaves organization_user_usage rows, so member daily-usage UI still shows pre-reset spend

SUGGESTION

File Line Issue
apps/web/src/app/api/cron/sales-demo-reset/route.ts 57 Per-org restore failures are swallowed with no log or Sentry event; cron still returns 200
Files Reviewed (28 files)
  • apps/web/src/app/(app)/components/AppSidebar.tsx - 0 issues
  • apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminDashboard.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminSalesDemoReset.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationsTable.tsx - 0 issues
  • apps/web/src/app/admin/organizations/page.tsx - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.test.ts - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.ts - 1 issue
  • apps/web/src/components/organizations/OrganizationDashboard.tsx - 0 issues
  • apps/web/src/components/organizations/SeatUsageCard.tsx - 0 issues
  • apps/web/src/lib/organizations/organization-types.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.test.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-email.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.ts - 1 issue
  • apps/web/src/lib/promoCreditCategories.ts - 0 issues
  • apps/web/src/lib/user/server.test.ts - 0 issues
  • apps/web/src/lib/user/server.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.test.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.ts - 0 issues
  • apps/web/src/routers/organizations/organization-admin-router.ts - 0 issues
  • apps/web/src/routers/organizations/sales-demo-admin-list.test.ts - 0 issues
  • apps/web/vercel.json - 0 issues
  • packages/db/src/schema-types.ts - 0 issues

Previous review (commit 4074c19)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 1

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/routers/organizations/organization-admin-router.ts 1269 NOT (settings->>'is_sales_demo' = 'true') is NULL when the key is absent, so ordinary trial orgs disappear from the trial admin list
apps/web/src/lib/organizations/sales-demo.ts 213 Restore leaves organization_user_usage rows, so member daily-usage UI still shows pre-reset spend

SUGGESTION

File Line Issue
apps/web/src/app/api/cron/sales-demo-reset/route.ts 57 Per-org restore failures are swallowed with no log or Sentry event; cron still returns 200
Files Reviewed (28 files)
  • apps/web/src/app/(app)/components/AppSidebar.tsx - 0 issues
  • apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminDashboard.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminSalesDemoReset.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationsTable.tsx - 0 issues
  • apps/web/src/app/admin/organizations/page.tsx - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.test.ts - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.ts - 1 issue
  • apps/web/src/components/organizations/OrganizationDashboard.tsx - 0 issues
  • apps/web/src/components/organizations/SeatUsageCard.tsx - 0 issues
  • apps/web/src/lib/organizations/organization-types.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.test.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-email.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.ts - 1 issue
  • apps/web/src/lib/promoCreditCategories.ts - 0 issues
  • apps/web/src/lib/user/server.test.ts - 0 issues
  • apps/web/src/lib/user/server.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.test.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.ts - 0 issues
  • apps/web/src/routers/organizations/organization-admin-router.ts - 1 issue
  • apps/web/src/routers/organizations/sales-demo-admin-list.test.ts - 0 issues
  • apps/web/vercel.json - 0 issues
  • packages/db/src/schema-types.ts - 0 issues

Previous review (commit ab4c233)

Status: 8 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 6
SUGGESTION 2

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/organizations/sales-demo.ts 211 Restore leaves organization_user_usage rows, so member daily-usage UI still shows pre-reset spend
apps/web/src/lib/organizations/sales-demo.ts 217 Concurrent admin reset and daily cron can delete credits then both grant $50
apps/web/src/lib/organizations/sales-demo.ts 226 Restore does not disable auto_top_up_enabled; next spend below $50 can charge a card saved during the demo
apps/web/src/lib/organizations/sales-demo.ts 238 Restore does not clear pending invitations, so invitees can rejoin after reset
apps/web/src/routers/organizations/organization-admin-router.ts 1260 Live demo orgs still match mode === 'trial', so used demos appear on both paying and trial admin lists
apps/web/src/components/organizations/SeatUsageCard.tsx 45 if (!org) return null runs before orgError, swallowing Seat Usage errors for every org

SUGGESTION

File Line Issue
apps/web/src/app/api/cron/sales-demo-reset/route.ts 57 Per-org restore failures are swallowed with no log or Sentry event; cron still returns 200
apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx 119 Overlay/Escape close leaves stale email and conflict state
Files Reviewed (28 files)
  • apps/web/src/app/(app)/components/AppSidebar.tsx - 0 issues
  • apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx - 1 issue
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminDashboard.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminSalesDemoReset.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationsTable.tsx - 0 issues
  • apps/web/src/app/admin/organizations/page.tsx - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.test.ts - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.ts - 1 issue
  • apps/web/src/components/organizations/OrganizationDashboard.tsx - 0 issues
  • apps/web/src/components/organizations/SeatUsageCard.tsx - 1 issue
  • apps/web/src/lib/organizations/organization-types.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.test.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-email.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.ts - 4 issues
  • apps/web/src/lib/promoCreditCategories.ts - 0 issues
  • apps/web/src/lib/user/server.test.ts - 0 issues
  • apps/web/src/lib/user/server.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.test.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.ts - 0 issues
  • apps/web/src/routers/organizations/organization-admin-router.ts - 1 issue
  • apps/web/src/routers/organizations/sales-demo-admin-list.test.ts - 0 issues
  • apps/web/vercel.json - 0 issues
  • packages/db/src/schema-types.ts - 0 issues

Previous review (commit a39c67a)

Status: 8 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 6
SUGGESTION 2

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/organizations/sales-demo.ts 214 Restore leaves organization_user_usage rows, so member daily-usage UI still shows pre-reset spend
apps/web/src/lib/organizations/sales-demo.ts 220 Concurrent admin reset and daily cron can delete credits then both grant $50
apps/web/src/lib/organizations/sales-demo.ts 228 Restore does not disable auto_top_up_enabled; next spend below $50 can charge a card saved during the demo
apps/web/src/lib/organizations/sales-demo.ts 241 Restore does not clear pending invitations, so invitees can rejoin after reset
apps/web/src/routers/organizations/organization-admin-router.ts 1251 Live demo orgs still match mode === 'trial', so used demos appear on both paying and trial admin lists
apps/web/src/components/organizations/SeatUsageCard.tsx 45 if (!org) return null runs before orgError, swallowing Seat Usage errors for every org

SUGGESTION

File Line Issue
apps/web/src/app/api/cron/sales-demo-reset/route.ts 57 Per-org restore failures are swallowed with no log or Sentry event; cron still returns 200
apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx 119 Overlay/Escape close leaves stale email and conflict state
Files Reviewed (28 files)
  • apps/web/src/app/(app)/components/AppSidebar.tsx - 0 issues
  • apps/web/src/app/admin/components/CreateDemoOrganizationDialog.tsx - 1 issue
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminDashboard.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationAdmin/OrganizationAdminSalesDemoReset.tsx - 0 issues
  • apps/web/src/app/admin/components/OrganizationsTable.tsx - 0 issues
  • apps/web/src/app/admin/organizations/page.tsx - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.test.ts - 0 issues
  • apps/web/src/app/api/cron/sales-demo-reset/route.ts - 1 issue
  • apps/web/src/components/organizations/OrganizationDashboard.tsx - 0 issues
  • apps/web/src/components/organizations/SeatUsageCard.tsx - 1 issue
  • apps/web/src/lib/organizations/organization-types.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.test.ts - 0 issues
  • apps/web/src/lib/organizations/organizations.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-email.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo-sort.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.test.ts - 0 issues
  • apps/web/src/lib/organizations/sales-demo.ts - 4 issues
  • apps/web/src/lib/promoCreditCategories.ts - 0 issues
  • apps/web/src/lib/user/server.test.ts - 0 issues
  • apps/web/src/lib/user/server.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.test.ts - 0 issues
  • apps/web/src/routers/admin/sales-demo-router.ts - 0 issues
  • apps/web/src/routers/organizations/organization-admin-router.ts - 1 issue
  • apps/web/src/routers/organizations/sales-demo-admin-list.test.ts - 0 issues
  • apps/web/vercel.json - 0 issues
  • packages/db/src/schema-types.ts - 0 issues

Reviewed by grok-4.6 · Input: 101.8K · Output: 14.9K · Cached: 697.3K

Review guidance: REVIEW.md from base branch main

Serialize resets with a row lock, disable auto top-up, and clear pending invitations so a reset demo is clean.
Demo orgs already appear on the paying list; stop them also matching the trial filter.
Render the error card on org-query failure instead of silently returning null.
Reset state on any close and use an email-literal-guardrail-safe placeholder.
IS DISTINCT FROM 'true' keeps ordinary orgs whose settings lack the key.
@iscekic iscekic added human-ready The PR is ready for human review. and removed human-ready The PR is ready for human review. labels Aug 25, 2026

@pandemicsyn pandemicsyn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few inline from the bot

Comment thread apps/web/src/lib/organizations/sales-demo.ts
Comment thread apps/web/src/lib/organizations/sales-demo.ts Outdated
Comment thread apps/web/src/lib/organizations/sales-demo.ts
@iscekic iscekic removed the human-ready The PR is ready for human review. label Aug 25, 2026
# Conflicts:
#	packages/db/src/migrations/meta/0224_snapshot.json
#	packages/db/src/migrations/meta/_journal.json
@iscekic iscekic added the human-ready The PR is ready for human review. label Aug 25, 2026
@iscekic
iscekic merged commit 10356e0 into main Aug 25, 2026
48 checks passed
@iscekic
iscekic deleted the demo-org-e8bd branch August 25, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants